package top.conangao.common.security.autoconfig;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import top.conangao.common.security.handler.CustomReactiveOpaqueTokenIntrospector;
import top.conangao.common.security.util.SecurityUtils;

/**
 * @author ConanGao
 * @description
 * @since 2024/2/22 11:01
 **/
@Configuration(proxyBeanMethods = false)
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@ConditionalOnClass(OAuth2ResourceServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@Slf4j
public class ResourceReactiveServerConfig {
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE + 1000)
    @ConditionalOnMissingBean(name = "defaultSecurityFilterChain")
    public SecurityWebFilterChain defaultSecurityFilterChain(ServerHttpSecurity http) {
        http.csrf(ServerHttpSecurity.CsrfSpec::disable)
            .cors(Customizer.withDefaults())
            .authorizeExchange(authorize -> authorize.pathMatchers("/oauth2/**", "/userinfo", "/logout").permitAll()
                                                     .anyExchange().authenticated())
            .oauth2ResourceServer(resourceServer -> resourceServer.opaqueToken(Customizer.withDefaults())
                                                                  .accessDeniedHandler(SecurityUtils::reactiveExceptionHandler)
                                                                  .authenticationEntryPoint(SecurityUtils::reactiveExceptionHandler));
        return http.build();
    }

    @Bean
    @Primary
    @ConditionalOnProperty(
            name = {"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri"}
    )
    public ReactiveOpaqueTokenIntrospector introspector(OAuth2ResourceServerProperties properties) {
        OAuth2ResourceServerProperties.Opaquetoken opaqueToken = properties.getOpaquetoken();
        return new CustomReactiveOpaqueTokenIntrospector(opaqueToken.getIntrospectionUri(),
                opaqueToken.getClientId(), opaqueToken.getClientSecret());
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration config = new CorsConfiguration();
        // 允许所有域名跨域
        config.addAllowedOriginPattern(CorsConfiguration.ALL);
        //允许所有请求头
        config.addAllowedHeader(CorsConfiguration.ALL);
        //允许所有方法
        config.addAllowedMethod(CorsConfiguration.ALL);
        // 允许证书
        config.setAllowCredentials(true);
        // 每一个小时，异步请求都发起预检请求 => 发送两次请求 第一次OPTION 第二次GET/POT/PUT/DELETE
        config.setMaxAge(3600L);
        // 添加映射路径，我们拦截一切请求
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        log.info("===========CORS配置成功=========");
        return source;
    }
}
